/** @file session_socket.c
  *
  * @brief This file contains the artosyn rpc socket node function calls.
  * Waiting for move daemon here.
  *
  *  Copyright (C) 2024, Artosyn Technologies. ALL RIGHTS RESERVED.
  *
  *  This Information is proprietary to Artosyn Technologies and MAY NOT
  *  be copied by any method or incorporated into another program without
  *  the express written consent of Artosyn. This Information or any portion
  *  thereof remains the property of Artosyn. The Information contained herein
  *  is believed to be accurate and Artosyn assumes no responsibility or
  *  liability for its use in any way and conveys no license or title under
  *  any patent or copyright and makes no representation or warranty that this
  *  Information is free from patent or copyright infringement.
  *
  */

/****************************
 * Include
 ****************************/
#include "rpc/ar_rpc.h"
#include "rpc/session_socket.h"
#include "net/net_dev.h"
#include "utils/utils.h"
#include "oal_workqueue.h"

/** BB socket tx msg */
typedef struct ar_socket_rpc_tx_msg_s {
    u8 slot;
    u32 port;
    u32 flags;
    void* priv;
} ar_socket_rpc_tx_msg_t;

/**
 * @brief  Take pkt from skb queue and ready sending to bus.
 *
 *  @param   handle             Point to the oal_handle
 *  @param   priv               Refers to ar_socket_session_t.
 *
 *  @return         N/A
 */
static void
ar_rpc_socket_tx_work_func(oal_handle *handle, void *priv)
{
    int ret = -1;
    struct sk_buff *skb = NULL;
    struct sk_buff *retry_skb = NULL;
    gfp_t flag;
    struct socket_msg_ret retmsg;
    ar_socket_rpc_tx_msg_t *ar_socket_rpc_tx_msg = NULL;
    ar_socket_session_t *socket_session = (ar_socket_session_t*)priv;

    AR_DBG(DM_NET_DEV, DL_INF, AR_FN_ENTRY_STR);

    if (!socket_session) {
        return;
    }

    if (!handle) {
        return;
    }

    /** handle tx packet */
    while ((skb = skb_dequeue(&socket_session->socket_tx_pkt_list)) != NULL) {
        ar_socket_rpc_tx_msg = (ar_socket_rpc_tx_msg_t *)(&(skb->cb));
        flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
        retry_skb = skb_copy(skb, flag);

        socket_session->tx_status = BB_SOCKET_SESSION_TX_STATUS_SENDING;
        /** bb socket write until success */
        ret = ar_rpc_socket_write(handle,
                            ar_socket_rpc_tx_msg->slot,
                            ar_socket_rpc_tx_msg->port,
                            skb,
                            ar_socket_rpc_tx_msg->flags,
                            ar_socket_rpc_tx_msg->priv,
                            -1);
        if (ret < 0) {
            dev_kfree_skb_any(skb);
            dev_kfree_skb_any(retry_skb);
            socket_session->tx_status = BB_SOCKET_SESSION_TX_STATUS_IDLE;
            continue;
        }

        socket_session->tx_status = BB_SOCKET_SESSION_TX_STATUS_WAIT_FOR_ACK;

        ret = wait_event_interruptible_timeout(socket_session->wait_q,
                                                (socket_session->tx_status == BB_SOCKET_SESSION_TX_STATUS_ACK_DONE),
                                                50);
        if (ret > 0) {
            /** receive the ack */
            switch (socket_session->ack_sta) {
                case -0x106:
                    /** send ok */
                    dev_kfree_skb_any(retry_skb);
                    break;
                case -0x107:
                    socket_session->buf_wr_index -= socket_session->retmsg.len;
                    /** tx buffer full, resend the skb */
                    if (retry_skb) {
                        ar_socket_rpc_tx_msg = (ar_socket_rpc_tx_msg_t *)(&(retry_skb->cb));
                        skb_queue_head(&socket_session->socket_tx_pkt_list, retry_skb);
                    }
                    break;
                case -0x108:
                    //1error
                    socket_session->buf_wr_index = socket_session->retmsg.len;
                    dev_kfree_skb_any(retry_skb);
                    break;
                default:
                    dev_kfree_skb_any(retry_skb);
                    break;
            }
        }
        else {
            /** just ignore */
            AR_DBG(DM_NET_DEV, DL_WRN, "Lost socket slot %d port %d ack!\n",
                                        socket_session->slot, socket_session->port);
        }

        socket_session->tx_status = BB_SOCKET_SESSION_TX_STATUS_IDLE;
    }

    AR_DBG(DM_NET_DEV, DL_INF, AR_FN_LEAVE_STR);
}

/**
 * @brief Init the socket session structure with slot/port/flag
 *
 * @param   handle  Point to the oal_handle
 * @param   socket  BB socket session info
 * @param   slot    slot of the bb socket
 * @param   port    port of the bb socket
 * @param   flag    flag of the bb socket
 *
 * @return  N/A
 */
static void
socket_init(oal_handle *handle, ar_socket_session_t* socket, int slot, int port, int flag)
{
    int ret = 0;
    oal_workqueue *socket_session_tx_work_queue = NULL;
    struct ar_rpc *rpc = NULL;
    unsigned long flags;

    if (!handle) {
        return;
    }

    socket->slot            = slot;
    socket->port            = port;
    socket->flags           = flag;
    socket->ack_sta         = 0;
    socket->work_id         = 0;
    socket->buf_wr_index    = 0;
    socket->tx_status       = BB_SOCKET_SESSION_TX_STATUS_IDLE;
    memset(&socket->retmsg, 0, sizeof(struct socket_msg_ret));

    /* Create workqueue for socket tx session process */
    ret = oal_init_workqueue(handle, &socket_session_tx_work_queue, "SOCKET_TX_WORK_QUEUE", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, ar_rpc_socket_tx_work_func, (void*)socket);
    if (ret != OAL_STATUS_SUCCESS) {
        AR_DBG(DM_GENERIC, DL_ERR, "%s: socket session tx workqueue init failed\n", __func__);
    }
    else {
        socket->socket_tx_workqueue = (void*)socket_session_tx_work_queue;
    }

    skb_queue_head_init(&socket->socket_tx_pkt_list);

    /** Initialize the wait queue */
    init_waitqueue_head(&socket->wait_q);

    spin_lock_irqsave(&handle->ar_rpc_lock, flags);
    rpc = (struct ar_rpc *)handle->ar_rpc;
    if (!rpc || (rpc->state == AR_RPC_STATE_DOWN)) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return;
    }
    atomic_inc(&handle->ar_rpc_ref_cnt);
    spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);

    spin_lock_irqsave(&rpc->op_spinlock, flags);
    /** Add node to the list */
    list_add_tail(&socket->list, &rpc->session_socket_list);
    spin_unlock_irqrestore(&rpc->op_spinlock, flags);

    atomic_dec(&handle->ar_rpc_ref_cnt);
}

/**
 * @brief Create a socket seesion to ar8030
 *
 * @param   handle  Point to the oal_handle
 * @param   slot    slot of the bb socket
 * @param   port    port of the bb socket
 * @param   flag    flag of the bb socket
 *
 * @return  BB socket session structure.
 */
ar_socket_session_t*
create_socket_session(oal_handle *handle, int slot, int port, int flag)
{
    ar_socket_session_t* socket = NULL;
    gfp_t gfp_flag;

    if (!handle) {
        return NULL;
    }

    gfp_flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;

    socket = (ar_socket_session_t*)kzalloc(sizeof(ar_socket_session_t), gfp_flag);

    if (socket) {
        socket_init(handle, socket, slot, port, flag);
    }

    return socket;
}

/**
 * @brief Remobe a socket seesion to ar8030
 *
 * @param   handle  Point to the oal_handle
 * @param   socket  BB socket session info
 *
 * @return  N/A.
 */
void remove_socket_session(oal_handle *handle, ar_socket_session_t* socket)
{
    struct list_head *list;
    struct ar_rpc *rpc = NULL;
    unsigned long flags;

    if (!handle) {
        return;
    }

    spin_lock_irqsave(&handle->ar_rpc_lock, flags);

    if (!handle->ar_rpc) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return;
    }

    rpc = (struct ar_rpc *)handle->ar_rpc;
    if (!rpc) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return;
    }

    if (rpc->state == AR_RPC_STATE_DOWN) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return;
    }
    atomic_inc(&handle->ar_rpc_ref_cnt);
    spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);

    if (socket) {
        spin_lock_irqsave(&rpc->op_spinlock, flags);
        list_del(&socket->list);
        spin_unlock_irqrestore(&rpc->op_spinlock, flags);

        /** Deinit the work queue */
        if (socket->socket_tx_workqueue) {
            oal_stop_workqueue(handle, socket->socket_tx_workqueue);
            oal_deinit_workqueue(handle, socket->socket_tx_workqueue);
            socket->socket_tx_workqueue = NULL;
        }

        skb_queue_purge(&socket->socket_tx_pkt_list);
        kfree(socket);
    }
    atomic_dec(&handle->ar_rpc_ref_cnt);
}

/**
 * @brief Get the socket seesion with slot and port
 *
 * @param   handle  Point to the oal_handle
 * @param   slot    slot of the bb socket
 * @param   port    port of the bb socket
 *
 * @return  ar_socket_session if find out, otherwise return NULL.
 */
static ar_socket_session_t* get_socket_session(oal_handle *handle, int slot, int port)
{
    struct ar_rpc *rpc = NULL;
    struct list_head *list = NULL;
    unsigned long flags;
    ar_socket_session_t *cur_socket_session = NULL, *socket_session = NULL;

    if (!handle) {
        return NULL;
    }

    spin_lock_irqsave(&handle->ar_rpc_lock, flags);

    if (!handle->ar_rpc) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return NULL;
    }

    rpc = (struct ar_rpc *)handle->ar_rpc;
    if (!rpc) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return NULL;
    }

    if (rpc->state == AR_RPC_STATE_DOWN) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return NULL;
    }

    atomic_inc(&handle->ar_rpc_ref_cnt);
    spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);

    spin_lock_irqsave(&rpc->op_spinlock, flags);

    list_for_each(list, &rpc->session_socket_list)
    {
        cur_socket_session = (ar_socket_session_t *)list;
        if ((cur_socket_session->slot == slot) &&
            (cur_socket_session->port == port))
        {
            socket_session = cur_socket_session;
            break;
        }
    }

    spin_unlock_irqrestore(&rpc->op_spinlock, flags);

    atomic_dec(&handle->ar_rpc_ref_cnt);

    return socket_session;
}

/**
 * @brief Inform the socket session when receiving the tx ack
 *
 * @param   handle  Point to the oal_handle
 * @param   slot    slot of the bb socket
 * @param   port    port of the bb socket
 * @param   sta     return value
 * @param   retmsg  return msg
 *
 * @return  N/A
 */
void socket_tx_ack_inform(oal_handle *handle, int slot, int port, int sta, struct socket_msg_ret *retmsg)
{
    ar_socket_session_t* socket_session = NULL;

    socket_session = get_socket_session(handle, slot, port);
    if (!socket_session) {
        return;
    }

    socket_session->ack_sta = sta;
    socket_session->tx_status = BB_SOCKET_SESSION_TX_STATUS_ACK_DONE;
    if (retmsg) {
        memcpy(&socket_session->retmsg, retmsg, sizeof(struct socket_msg_ret));
    }
    wake_up_interruptible_all(&socket_session->wait_q);
}

/**
 * @brief  Push pkt to the skb queue of the socket rpc tx.
 *
 *  @param handle   Point to the oal_handle
 *  @param   slot            slot of the bb socket
 *  @param   port            port of the bb socket
 *  @param   skb             skb of the data
 *  @param   priv            private data
 *
 *  @return         N/A
 */
int
ar_rpc_socket_tx(oal_handle *handle, u8 slot, u32 port, struct sk_buff *skb, bool datagram_mode, void* priv)
{
    int ret = 0;
    ar_socket_session_t* socket_session = NULL;
    ar_socket_rpc_tx_msg_t ar_socket_rpc_tx_msg = {0};

    if (!handle) {
        return -1;
    }

    if (!skb) {
        return -2;
    }

    socket_session = get_socket_session(handle, slot, port);
    if (!socket_session) {
        return -3;
    }

    ar_socket_rpc_tx_msg.slot = slot;
    ar_socket_rpc_tx_msg.port = port;
    ar_socket_rpc_tx_msg.flags = datagram_mode;
    ar_socket_rpc_tx_msg.priv = priv;

    memcpy(skb->cb, &ar_socket_rpc_tx_msg, sizeof(ar_socket_rpc_tx_msg_t));

    skb_queue_tail(&socket_session->socket_tx_pkt_list, skb);
    oal_queue_work(handle, socket_session->socket_tx_workqueue);

    return ret;
}

/**
 * @brief Assemble the bb socket open msg to 8030
 *
 * @param   handle          Point to the oal_handle
 * @param   slot            slot of the bb socket
 * @param   port            port of the bb socket
 * @param   flg             flag of the bb socket
 * @param   tx_buf_size     tx_buf_size of the bb socket
 * @param   rx_buf_size     rx_buf_size of the bb socket
 *
 * @return  0 for success, otherwise fail.
 */
int
ar_bb_rpc_socket_open(oal_handle* handle, u8 slot, u32 port, u32 flg, u32 tx_buf_size, u32 rx_buf_size)
{
    int      ret = 0;
    u32      req = 4 << 24 | so_open << 16 | slot << 8 | port << 0;
    u32      flag;
    bb_sock_opt_t tmpopt;
    rpc_pack_t pack = {
        .reqid      = req
    };
    struct sk_buff *skb = NULL;
#ifdef AR_CONFIG_RPC_WITH_REPLY
    int     wr_len = 0;
    wake_up wu     = {
            .wkf = wk_wr,
            .prv = &wr_len,
    };
#endif

    if (!handle) {
        AR_DBG(DM_RPC, DL_ERR, "%s:handle is NULL!\n", __FUNCTION__);
        return -ENOENT;
    }

    /* flag + bb_sock_opt_t */
    skb = dev_alloc_skb(fixpackbase + sizeof(u32) + sizeof(bb_sock_opt_t));
    if (!skb) {
        AR_DBG(DM_USB, DL_WRN, "%s: Error! allocate skb failed!\n", __func__);
        return -ENOMEM;
    }

    /* Reserve the head room */
    skb_reserve(skb, data_offset);

    AR_DBG(DM_RPC, DL_MSG, "rx_buf_size %d tx_buf_size %d flg 0x%x\n", rx_buf_size, tx_buf_size, flg);

    if ((flg & (BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX)) == 0) {
        AR_DBG(DM_RPC, DL_ERR, "bb socket open using err flg 0x%x\n", flg);
        return -EPERM;
    }

    tmpopt.rx_buf_size = rx_buf_size;
    tmpopt.tx_buf_size = tx_buf_size;

    flag = flg & (BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX);

    /** Fill the msg payload */
    skb_put(skb, sizeof(u32) + sizeof(bb_sock_opt_t));
    memcpy(skb->data + 0, &flg, sizeof(u32));
    memcpy(skb->data + sizeof(u32), &tmpopt, sizeof(bb_sock_opt_t));

    ret = ar_rpc_tx_msg(handle, &pack, skb);
    if (ret < 0) {
        AR_DBG(DM_RPC, DL_ERR, "ar socket open failed, ret %d\n", ret);
    }
    else {
        ret = 0;
    }

    mdelay(20);

    return ret;
}

/**
 * @brief Assemble the bb socket close msg to 8030
 *
 * @param   handle          Point to the oal_handle
 * @param   slot            slot of the bb socket
 * @param   port            port of the bb socket
 * @param   flg             flag of the bb socket
 *
 * @return  0 for success, otherwise fail.
 */
int
ar_rpc_socket_close(oal_handle* handle, u8 slot, u32 port, u32 flg)
{
    int      ret = 0;
    u32      req = 4 << 24 | so_close << 16 | slot << 8 | port << 0;
    u32      flag;
    bb_sock_opt_t tmpopt;
    rpc_pack_t pack = {
        .reqid      = req
    };
    struct sk_buff *skb = NULL;
#ifdef AR_CONFIG_RPC_WITH_REPLY
    int     wr_len = 0;
    wake_up wu     = {
            .wkf = wk_wr,
            .prv = &wr_len,
    };
#endif

    if (!handle) {
        AR_DBG(DM_RPC, DL_ERR, "%s:handle is NULL!\n", __FUNCTION__);
        return -ENOENT;
    }

    /* flag + bb_sock_opt_t */
    skb = dev_alloc_skb(fixpackbase + sizeof(u32));
    if (!skb) {
        AR_DBG(DM_USB, DL_WRN, "%s: Error! allocate skb failed!\n", __func__);
        return -ENOMEM;
    }

    /* Reserve the head room */
    skb_reserve(skb, data_offset);

    if ((flg & (BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX)) == 0) {
        AR_DBG(DM_RPC, DL_ERR, "bb socket open using err flg 0x%x\n", flg);
        return -EPERM;
    }

    flag = flg & (BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX);

    skb_put(skb, sizeof(u32));

    memcpy(skb->data + 0, &flg, sizeof(u32));

    ret = ar_rpc_tx_msg(handle, &pack, skb);
    if (ret < 0) {
        AR_DBG(DM_RPC, DL_ERR, "ar socket open failed, ret %d\n", ret);
    }
    else {
        ret = 0;
    }

    /** Wait for socket close done */
    mdelay(500);

    return ret;
}

/**
 * @brief Assemble the bb socket write msg to 8030
 *
 * @param   handle          Point to the oal_handle
 * @param   slot            slot of the bb socket
 * @param   port            port of the bb socket
 * @param   skb             skb of the data
 * @param   priv_data       private data for tx callback
 * @param   timeout         timeout of the operation
 *
 * @return  0 for success, otherwise fail.
 */
static int
_ar_rpc_bb_socket_write(oal_handle* handle, u8 slot, u32 port, struct sk_buff *skb, void *priv_data, struct timespec* timeout)
{
    int ret;
    u8 *data = NULL;
    int len = 0;
    u32 req = 4 << 24 | so_write << 16 | slot << 8 | port << 0;
    struct ar_rpc *rpc = NULL;
    unsigned long flags;
    ar_socket_session_t* socket_session = NULL;

    rpc_pack_t pack = {
        .reqid   = req,
    };

    if (!handle) {
        AR_DBG(DM_RPC, DL_ERR, "%s %d handle null!!\n",__FUNCTION__,__LINE__);
        return -1;
    }


    socket_session = get_socket_session(handle, slot, port);
    if (!socket_session) {
        return -2;
    }

    if (!skb) {
        AR_DBG(DM_RPC, DL_ERR, "%s %d skb null!!\n",__FUNCTION__,__LINE__);
        return -3;
    }

    spin_lock_irqsave(&handle->ar_rpc_lock, flags);

    if (!handle->ar_rpc) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return -4;
    }

    rpc = (struct ar_rpc *)handle->ar_rpc;
    if (!rpc) {
        AR_DBG(DM_RPC, DL_ERR, "%s %d rpc null!!\n",__FUNCTION__,__LINE__);
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return -5;
    }

    if (rpc->state == AR_RPC_STATE_DOWN) {
        spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);
        return -6;
    }

    atomic_inc(&handle->ar_rpc_ref_cnt);
    spin_unlock_irqrestore(&handle->ar_rpc_lock, flags);

    pack.msgid = socket_session->work_id++;

    /* add socket hdr */
    len = skb->len;
    skb_push(skb, sizeof(uint64_t));
    data = (u8 *)skb->data;
    memcpy(data, &socket_session->buf_wr_index, sizeof(uint64_t));

    oal_spin_lock(handle, rpc->op_wr_spinlock);
    ret = ar_rpc_tx_pkt(handle, &pack, skb, priv_data);
    if (ret < 0) {
        AR_DBG(DM_RPC, DL_VRB, "ar socket write failed, ret %d\n", ret);
    }
    oal_spin_unlock(handle, rpc->op_wr_spinlock);

    atomic_dec(&handle->ar_rpc_ref_cnt);

    socket_session->buf_wr_index += len;

    return ret;
}

/**
 * @brief Assemble the bb socket write skb, if there's no enough
 * skb headroom/tailroom, realloc the skb.
 *
 * @param   handle          Point to the oal_handle
 * @param   slot            slot of the bb socket
 * @param   port            port of the bb socket
 * @param   skb             skb of the data
 * @param   datagram_mode   Whether send as datagram mode
 * @param   priv_data       private data for tx callback
 * @param   timeout         timeout of the operation
 *
 * @return  0 for success, otherwise fail.
 */
int
ar_rpc_socket_write(oal_handle* handle, u8 slot, u32 port, struct sk_buff *skb, bool datagram_mode, void *priv_data, int timeout)
{
    u32        curlen = 0;
    int   ret  = 0;
    u8 *data;
    int len = 0;    //len of payload
    gfp_t flag;
    bool b_need_expand = false;
    int expand_hdr = 0, expand_tail = 0;
    struct sk_buff *tx_skb = NULL, *free_skb = NULL;

    if (!skb) {
        AR_DBG(DM_RPC, DL_ERR, "%s %d skb null!!\n",__FUNCTION__,__LINE__);
        return -1;
    }

    if (datagram_mode) {
        len = skb->len;

        if ((skb_tailroom(skb) < AR_NET_TX_MAX_TAILROOM) && (skb_headroom(skb) < AR_NET_TX_MAX_HEADROOM)) {
            b_need_expand = true;
            expand_hdr = AR_NET_TX_MAX_HEADROOM;
            expand_tail = AR_NET_TX_MAX_TAILROOM;
        }
        else if (skb_headroom(skb) < AR_NET_TX_MAX_HEADROOM) {
            b_need_expand = true;
            expand_hdr = AR_NET_TX_MAX_HEADROOM;
            expand_tail = skb_tailroom(skb);
        }
        else if (skb_tailroom(skb) < AR_NET_TX_MAX_TAILROOM) {
            b_need_expand = true;
            expand_hdr = skb_headroom(skb);
            expand_tail = AR_NET_TX_MAX_TAILROOM;
        }
        else if (skb->cloned) {
            b_need_expand = true;
            expand_hdr = AR_NET_TX_MAX_HEADROOM;
            expand_tail = AR_NET_TX_MAX_TAILROOM;
        }

        if (b_need_expand) {
            flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
            tx_skb = skb_copy_expand(skb, expand_hdr, expand_tail, flag);
            free_skb = skb;
        }
        else {
            tx_skb = skb;
        }

        if (!tx_skb) {
            AR_DBG(DM_RPC, DL_ERR, "%s %d tx_skb null!!\n",__FUNCTION__,__LINE__);
            return -2;
        }

        /*add datagram hdr*/
        skb_push(tx_skb, datagram_offset);
        /*add datagram tail*/
        skb_put(tx_skb, datagram_len - datagram_offset);
        data = (u8 *)tx_skb->data;

        make_datagram_pack2buff_nocopy(data, len);
    }
    else {
        if (skb->cloned) {
            flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
            tx_skb = skb_copy_expand(skb, skb_headroom(skb), skb_tailroom(skb), flag);
            free_skb = skb;
        }
        else {
            tx_skb = skb;
        }
    }

    AR_DBG(DM_RPC, DL_VRB, "%s tx_skb 0x%p(d 0x%p) skb 0x%p(d 0x%p) \n", __FUNCTION__, tx_skb, tx_skb->data, skb, skb->data);

    ret = _ar_rpc_bb_socket_write(handle, slot, port, tx_skb, priv_data, NULL);
    if (ret >= 0) {
        if (free_skb) {
            dev_kfree_skb_any(free_skb);
        }
    }
    else {
        if (tx_skb != skb) {
            dev_kfree_skb_any(tx_skb);
        }
    }

    return ret;
}
